/******************************************************************************

 @File         PVRShellAPI.cpp

 @Title        KEGL/PVRShellAPI

 @Version      

 @Copyright    Copyright (C)  Imagination Technologies Limited.

 @Platform     Independent

 @Description  Makes programming for 3D APIs easier by wrapping surface
               initialization, Texture allocation and other functions for use by a demo.

******************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>

#include "PVRShell.h"
#include "PVRShellAPI.h"
#include "PVRShellOS.h"
#include "PVRShellImpl.h"

#include <QDesktopWidget>
#include <QApplication>
/*****************************************************************************
	Declarations
*****************************************************************************/

/****************************************************************************
** Class: PVRShellInitAPI
****************************************************************************/

/*****************************************************************************
* Function Name  : ApiInitAPI
* Returns        : true for success
* Description    : Initialise the 3D API
*****************************************************************************/
bool PVRShellInit::ApiInitAPI()
{
    QMainWindow *pWindow = (QMainWindow*) OsGetNativeWindowType();

    m_pGLWidget = new PVRShellGLWidget(pWindow, m_pShell, m_pShell->m_pShellData);

    if(pWindow)
    {
        pWindow->setAttribute(Qt::WA_PaintOnScreen);
        pWindow->setCentralWidget(m_pGLWidget);
	}

    m_pGLWidget->makeCurrent();
    return true;
}

/*!***********************************************************************
@Function		OutputAPIInfo
@description	When prefOutputInfo is set to true this function outputs
				various pieces of API dependent information via
				PVRShellOutputDebug.
*************************************************************************/
void PVRShellInit::OutputAPIInfo()
{
	// Output API dependent information
    if(m_pShell->PVRShellGet(prefOutputInfo))
	{
		m_pShell->PVRShellOutputDebug("\n");
#ifndef BUILD_OVG
		m_pShell->PVRShellOutputDebug("GL:\n");
		m_pShell->PVRShellOutputDebug("  Vendor:   %s\n", (char*) glGetString(GL_VENDOR));
		m_pShell->PVRShellOutputDebug("  Renderer: %s\n", (char*) glGetString(GL_RENDERER));
		m_pShell->PVRShellOutputDebug("  Version:  %s\n", (char*) glGetString(GL_VERSION));
#else
		m_pShell->PVRShellOutputDebug("VG:\n");
		m_pShell->PVRShellOutputDebug("  Vendor:   %s\n", (char*) vgGetString(VG_VENDOR));
		m_pShell->PVRShellOutputDebug("  Renderer: %s\n", (char*) vgGetString(VG_RENDERER));
		m_pShell->PVRShellOutputDebug("  Version:  %s\n", (char*) vgGetString(VG_VERSION));
#endif

		m_pShell->PVRShellOutputDebug("\n");
        m_pShell->PVRShellOutputDebug("Qt:\n");
        m_pShell->PVRShellOutputDebug("  Version:  " QT_VERSION_STR "\n");

		m_pShell->PVRShellOutputDebug("\n");
		m_pShell->PVRShellOutputDebug("Window Width:  %i\n" , m_pShell->PVRShellGet(prefWidth));
		m_pShell->PVRShellOutputDebug("Window Height: %i\n" , m_pShell->PVRShellGet(prefHeight));
		m_pShell->PVRShellOutputDebug("Is Rotated: %s\n", m_pShell->PVRShellGet(prefIsRotated) ? "Yes" : "No");
		m_pShell->PVRShellOutputDebug("\n");

		// Colour buffer
        QGLFormat format = m_pGLWidget->context()->format();
        m_pShell->PVRShellOutputDebug("QGL Surface:\n");

        int i32Bits = format.redBufferSize() + format.greenBufferSize() + format.blueBufferSize() + format.alphaBufferSize();
        m_pShell->PVRShellOutputDebug("  Colour Buffer:  %i bits (R%i G%i B%i A%i)\n", i32Bits, format.redBufferSize(),format.greenBufferSize(),format.blueBufferSize(),format.alphaBufferSize());

		// Depth buffer
        m_pShell->PVRShellOutputDebug("  Depth Buffer:   %i bits\n", format.depthBufferSize());

		// Stencil Buffer
        m_pShell->PVRShellOutputDebug("  Stencil Buffer: %i bits\n", format.stencilBufferSize());

#ifndef BUILD_OVG
        m_pShell->PVRShellOutputDebug("  Samples per pixel: %i\n", format.samples());
#else
		m_pShell->PVRShellOutputDebug("\n");

		switch(vgGeti(VG_RENDERING_QUALITY))
		{
			case VG_RENDERING_QUALITY_BETTER:
				m_pShell->PVRShellOutputDebug("Rendering quality:  VG_RENDERING_QUALITY_BETTER\n");
			break;
			case VG_RENDERING_QUALITY_FASTER:
				m_pShell->PVRShellOutputDebug("Rendering quality:  VG_RENDERING_QUALITY_FASTER\n");
			break;
			default:
				m_pShell->PVRShellOutputDebug("Rendering quality:  VG_RENDERING_QUALITY_NONANTIALIASED\n");
		}

#endif
    }
}

/*!***********************************************************************
 @Function		ApiReleaseAPI
 @description	Releases all resources allocated by the API.
*************************************************************************/
void PVRShellInit::ApiReleaseAPI()
{
    // The window owns the GLwidget so just set our pointer to 0
    m_pGLWidget = 0;

    QMainWindow *pWindow = (QMainWindow*) OsGetNativeWindowType();

    if(pWindow)
        pWindow->setCentralWidget(0);
}

/*!***********************************************************************
@Function		ApiScreenCaptureBuffer
@Input			Width Width of the region to capture
@Input			Height Height of the region to capture
@Input			pBuf A buffer to put the screen capture into
@description	API-specific function to store the current content of the
				FrameBuffer into the memory allocated by the user.
*************************************************************************/
bool PVRShellInit::ApiScreenCaptureBuffer(int Width,int Height,unsigned char *pBuf)
{
	unsigned char	*pLines2;
	int				i, j;
	bool			bRet = true;

        // Allocate memory for line
	pLines2 = (unsigned char *)calloc(4 * Width * Height, sizeof(unsigned char));
	if (!pLines2) return false;

#ifdef BUILD_OVG
    while(vgGetError()){};

	vgReadPixels(pLines2, Width * 4, VG_sRGBA_8888, 0, 0, Width, Height);

	if(vgGetError())
	{
		bRet = false;
	}
	else
	{
		/* Convert RGB to BGR in line */
		for (j = 0, i = 0; j < 4 * Width * Height; j += 4, i += 3)
		{
			pBuf[i]   = pLines2[j+1];
			pBuf[i+1] = pLines2[j+2];
			pBuf[i+2] = pLines2[j+3];
		}
	}
#else
    while(glGetError()){};
	/* Read line from frame buffer */
	glReadPixels(0, 0, Width, Height, GL_RGBA, GL_UNSIGNED_BYTE, pLines2);

	if(glGetError())
	{
		bRet = false;
	}
	else
	{
		/* Convert RGB to BGR in line */
		for (j = 0, i = 0; j < 4 * Width * Height; j += 4, i += 3)
		{
			pBuf[i] = pLines2[j+2];
			pBuf[i+1] = pLines2[j+1];
			pBuf[i+2] = pLines2[j];
		}
	}
#endif

	free(pLines2);
	return bRet;
}

/*!***********************************************************************
 @Function		ApiRenderComplete
 @description	Perform API operations required after a frame has finished (e.g., flipping).
*************************************************************************/
void PVRShellInit::ApiRenderComplete()
{
    if(m_pShell->m_pShellData->bNoShellSwapBuffer)
            return;

    m_pGLWidget->swapBuffers();
}

/*!***********************************************************************
 @Function		ApiSet
 @Input			prefName	Name of value to set
 @Modified		i32Value	Value to set it to
 @description	Set parameters which are specific to the API.
*************************************************************************/
bool PVRShellInit::ApiSet(const prefNameIntEnum prefName, const int i32Value)
{
	switch(prefName)
	{
	case prefSwapInterval:
		m_pShell->m_pShellData->nSwapInterval = i32Value;
		return true;

	default:
		return false;
	}
}

/*!***********************************************************************
 @Function		ApiGet
 @Input			prefName	Name of value to get
 @Modified		pn A pointer set to the value asked for
 @description	Get parameters which are specific to the API.
*************************************************************************/
bool PVRShellInit::ApiGet(const prefNameIntEnum prefName, int *pn)
{
    PVRSHELL_UNREFERENCED_PARAMETER(prefName);
    PVRSHELL_UNREFERENCED_PARAMETER(pn);
    return false;
}

/*!***********************************************************************
 @Function		ApiGet
 @Input			prefName	Name of value to get
 @Modified		pp A pointer set to the value asked for
 @description	Get parameters which are specific to the API.
*************************************************************************/
bool PVRShellInit::ApiGet(const prefNamePtrEnum prefName, void **pp)
{
    PVRSHELL_UNREFERENCED_PARAMETER(prefName);
    PVRSHELL_UNREFERENCED_PARAMETER(pp);
    return false;
}

/****************************************************************************
** Local code
****************************************************************************/

PVRShellGLWidget::PVRShellGLWidget(QWidget *parent, PVRShell *pShell, const PVRShellData * pData) : m_pShell(pShell)
{
	QGLFormat format;

	if(pData)
	{
		switch(pData->nColorBPP)
		{
			case 16:
				format.setRedBufferSize(5);
				format.setGreenBufferSize(6);
				format.setBlueBufferSize(5);
				format.setAlphaBufferSize(0);
			break;
			case 24:
				format.setRedBufferSize(8);
				format.setGreenBufferSize(8);
				format.setBlueBufferSize(8);
				format.setAlphaBufferSize(0);
			break;
			case 32:
				format.setRedBufferSize(8);
				format.setGreenBufferSize(8);
				format.setBlueBufferSize(8);
				format.setAlphaBufferSize(8);
			break;
			default:
			{
				// Do nothing
			}
		}

		if(pData->bNeedZbuffer || pData->nDepthBPP > 0)
		{
            format.setDepth(true);
            format.setDepthBufferSize((pData->nDepthBPP > 0) ? pData->nDepthBPP : 16);
		}

		if(pData->bNeedStencilBuffer)
		{
            format.setStencil(true);
			format.setStencilBufferSize(8);
		}

#ifndef BUILD_OVG
		switch(pData->nFSAAMode)
		{
			case 1:
				format.setSampleBuffers(true);
				format.setSamples(2);
			break;

			case 2:
				format.setSampleBuffers(true);
				format.setSamples(4);
			break;

			default:
				format.setSampleBuffers(false);
		}
#endif
	}

    format.setSwapInterval(pData->nSwapInterval);

    QGLWidget::QGLWidget(format, parent);

    setAttribute(Qt::WA_PaintOnScreen);
    setAttribute(Qt::WA_NoSystemBackground);
    setAutoBufferSwap(false);
    setFocusPolicy(Qt::NoFocus);
}

PVRShellGLWidget::~PVRShellGLWidget()
{

}

void PVRShellGLWidget::resizeGL(int w, int h)
{
    m_pShell->PVRShellSet(prefWidth, w);
    m_pShell->PVRShellSet(prefHeight, h);

    glViewport(0,0,w,h);
}

QSize PVRShellGLWidget::sizeHint()
{
    QDesktopWidget *desktop = QApplication::desktop();
    return QSize(desktop->width(),desktop->height());
}

/*****************************************************************************
 End of file (PVRShellAPI.cpp)
*****************************************************************************/


